This is a race bug.
The problem is that the (private) next field is used for two different linked lists. The first list is the "Pending" list of reference objects, which the garbage collector prepares for the referenceHandler thread. The second linked list is the list that implements the queue of the reference objects. The user program may invoke the enqueue method on an object in the Pending list. If that happens, the Pending list and/or the queue may be destroyed.
More details about this bug are at JDK-4243978 JIRA page.
java.lang.ref.Reference$ReferenceHandler t1 t2 public void run() { ... * synchronized (lock) { if (pending != null) { r = pending; Reference rn = r.next; pending = (rn == r) ? null : rn; 2 r.next = r; ... } ... } ... } java.lang.ref.ReferenceQueueboolean enqueue(Reference<? extends T> r) { ... * synchronized (lock) { r.queue = ENQUEUED; 1 r.next = (head == null) ? r : head; head = r; ... } ... }
Precondition:
Althogh both methods have synchronized on lock at *, those locks are different instances in two classes.
head at 1 is the head element of a link list queue.
a) thread 1 put r in front of the queue at 1.
b) context switched, thread 2 set r.next to itself at 2. When the r at 2 is exactly the same instance as
the r at 1, r.next was the head of queue, but now it's r itself, and the whole queue is lost.
This bug is reproduced under JDK 1.6.0_33 and JDK 1.7.0.
It started at JDK 1.6.0, and has been fixed since JDK 1.8.0.
Execute the following scripts to run the test to reproduce the bug (assume the location of the jdk6 test project is jdk_test_home).
Linux:
${jdk_test_home}/scripts/4243978.sh [--javaloc path]
Windows:
%jdk_test_home%\scripts\4243978.bat [--javaloc path]
| Option | Function |
| --javaloc | The location of JDK that is eligible to reproduce the bug, if your java environment is not eligible.
In this case, it's JDK 1.6.0 or JDK1.7.0. It should be the absolute path to the JDK's java starter and ended with "/". For example: ~/jdk/home/jdk1.6.0/bin/ |